#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "FatStructs.h"
#include "common-libs/collections/list.h"
#include "TempFileFuncs.h" //remover cuando agreguemos sockets


static struct fat32BootSector BootSector;
static uint32_t* FAT;
static uint16_t SizeSector;
static uint32_t SizeCluster;
static uint32_t primerDataSector;
t_list clustersLibres;



void leerBootSector()
{
	fbs_t* BS    = (fbs_t *) leerSector( 0 );
	BootSector = *BS;

	printf("cantidad Bytes Por Sector : %u\n", BootSector.bytesPerSector);
	printf("cantidad Sectores Por Cluster : %u\n", BootSector.sectorsPerCluster);
	printf("cantidad FATs : %u\n", BootSector.fatCount);
	printf("cantidad Total de Sectores : %u\n", BootSector.totalSectors32);
	printf("cantidad Sectores Ocupados FAT : %u\n", BootSector.sectorsPerFat32);

	SizeSector = BootSector.bytesPerSector;
	SizeCluster = SizeSector * BootSector.sectorsPerCluster;
	primerDataSector = BootSector.reservedSectorCount + (BootSector.fatCount * BootSector.sectorsPerFat32);
}



void leerFatRegion()
{
	uint16_t DirFat =  BootSector.reservedSectorCount;
	uint32_t n = 0;

	free (FAT);
	FAT = (uint32_t*) malloc(SizeSector*BootSector.sectorsPerFat32);

	while ( n  < BootSector.sectorsPerFat32)
	{
		memcpy (((unsigned char*)FAT + SizeSector*n),leerSector (DirFat + n), SizeSector); //creo que ahora si funcionaria, pero hay que probar.
		n++;
	}
}



void obtenerClustersLibres()
{
	clustersLibres = *collection_list_create();
	uint32_t Ncluster;
	for (Ncluster = 0; Ncluster < BootSector.sectorsPerFat32; Ncluster++ )
	{
		if (FAT[Ncluster] == 0) collection_list_add (&clustersLibres, &Ncluster); //esta bien mandar la direccion? o hay que crear un puntero para mandarlo?
	}
}



uint32_t liberarCluster(uint32_t clusterN) //habria que agregar ordenado (para facilitar busqueda de clusters continuos), buscar en list.h o preguntarle a facundo
{
	uint32_t siguiente = FAT[clusterN];
	FAT[clusterN] = 0;

	collection_list_add (&clustersLibres, &clusterN);

	uint32_t n;
	for (n = 0; n < BootSector.sectorsPerFat32; n++) escribirUnSector(BootSector.reservedSectorCount, FAT);

	return siguiente;
}



//asignarClusters (cuantos, donde)
//Si "donde" es positivo, asigna los clusters despues de "donde".
//Si "donde" es negativo, asigna los clusters antes de "donde"
//Si "donde" es 0, es una cadena nueva
//Devuelve primer cluster asignado (seria el nuevo inicial de la cadena para 0 o negativo).

uint32_t asignarClusters(uint32_t cuantos, int64_t donde)
{
	uint32_t clusterPrimero= *(uint32_t*)collection_list_remove (&clustersLibres,0);
	uint32_t clusterN = clusterPrimero;
	uint32_t n;

	for (n = 1; n < cuantos; n++)
	{
		FAT[clusterN] = *(uint32_t*)collection_list_remove (&clustersLibres,0);
		clusterN = FAT[clusterN];
	}

	if (donde >  0) {FAT[clusterN]= FAT[donde];	FAT[donde] = clusterPrimero;}
	if (donde == 0) {FAT[clusterN]= FAT32EOC_MIN;}
	if (donde <  0) {FAT[clusterN]= donde;}

	for (n = 0; n < BootSector.sectorsPerFat32; n++) escribirUnSector(BootSector.reservedSectorCount,FAT);

	return clusterPrimero;

}



char* LeerCluster(uint32_t clusterN)
{
	uint64_t clusterSec = primerDataSector + (clusterN - 2) * BootSector.sectorsPerCluster;

	char* contenidoCluster = (char*) malloc(SizeCluster);
	uint8_t i;

	for(i = 0; i < BootSector.sectorsPerCluster; i++)
	{
		char * sector = leerSector(clusterSec);
		memcpy(contenidoCluster + i * SizeSector, sector, SizeSector);
		clusterSec++;
	}

	return contenidoCluster;
}


char* leerArchivoEnCluster(uint32_t cluster)
{

	uint32_t Ncluster = cluster;
	uint32_t cantClusters=0;

	while (Ncluster >= FAT32FIRST_VALID && Ncluster <= FAT32FIRST_VALID)
	{
		Ncluster=FAT[Ncluster];
		cantClusters++;
	}
// supongo que habria que devolver algun error si el ultimo cluster no es End of Chain. No se me ocurre una buena forma de devolverlo.

	char* archivo = malloc (SizeCluster*cantClusters);
	Ncluster = cluster;

	while (Ncluster >= FAT32FIRST_VALID && Ncluster <= FAT32FIRST_VALID)
	{
		memcpy (archivo, LeerCluster(Ncluster), SizeCluster); //cuando se libera el malloc de LeerCluster? lo mismo con leerSector y otras formulas que reservan espacio que persiste al terminar la funcion (por referencia)
		Ncluster=FAT[Ncluster];
	}

	return archivo;
}


/*   FALTA LA LIBRERIA DE UTF 16/8
uint32_t ClusterDeDirCluster (uint32_t DirCluster, char* Nombre, uint_8 (*eSTipoReq)(dir_t*) )	//compara nombres hasta encontrar, obtiene y lee el nuevo directorio y devuelve los entries.
{																						//es de orden superior, para pasar las funciones DIR_IS_SUBDIR o DIR_IS_FILE
	uint16_t i = 0;
	uint8_t ArchivoEncontrado = 0; //lo uso como booleano
	uint32_t ArchivoCluster = 0; //esto es lo que va a retornar si no encuentra
	dir_t* Entries = (dir_t*) leerArchivoEnCluster(DirCluster);
	char longName[13+1];

	while (!ArchivoEncontrado && Entries[i].name[0] != 0) if ((Entries[i]).name[0] != 0)
	{
		if (DIR_IS_LONG_NAME(Entries[i]))
		{
			dirL_t TempEntry = (dirL_t) Entries[i];
			memcpy (longName, utf8_16());

		}
	}
	return ArchivoCluster;
}

char* leerArchivoEnPath(char* DirPath) //lee archivo (o dir) en el Path. si se usa para leer un directorio, hay que castear a dir_t*
{
	dir_t* tempDir = (leerArchivoEnCluster(BootSector.fat32RootCluster));

	char* tempDirPath, save, tempDirName;
	strcpy (tempDirPath,DirPath);

	 while (tempDirName != NULL)
	{
		tempDirName = strtok_r(tempDirPath, "/", &save); //obtiene el nombre del proximo subdir
		if (tempDirName != NULL) tempDir = (dir_t*) obtenerArchivoDeDir (tempDirName, tempDir, DIR_IS_SUBDIR); //obtiene el subdir
	}
	 return tempDirName;
}
*/

/* MAIN DE PRUEBA
uint32_t main()
{
	leerBootSector();
	leerFatRegion(); // OBTENER 1073152

	char* Clust = (char*)malloc(512 * 8);
	Clust = LeerCluster(4);
    char* ENTRADAS = (char*)malloc(512 * 8 * 4);
    ENTRADAS = leerArchivoEnCluster(4);

    //printf("Cluster: %s\n", Clust);

    printf("Clusters: %s\n", ENTRADAS);
	return 0;
}

	return siguiente;
}

*/

uint32_t main()
{
	procesarConsolaFS();
	return 0;
}
